package com.hongji.lms5xxcommunicator.realtime.sync;

import com.hongji.lms5xxcommunicator.opc.client.OpcUaClientService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class OpcXSourceImp implements XSource {

    private final OpcUaClientService opc;
    private final XRingBuffer xBuff = new XRingBuffer(8192);

    @Value("${scanner.x.node_id}")      private String xNodeIdStr;
    @Value("${x.sample.period.ms:5}")   private int samplePeriodMs;

    // 统一坐标（来自 application.yml 的 scanner.coord.*）
    @Value("${scanner.coord.enabled:true}")   private boolean coordEnabled;
    @Value("${scanner.coord.x_scale:1.0}")    private double xScale;
    @Value("${scanner.coord.x_offset:0.0}")   private double xOffset;
    @Value("${scanner.coord.invert_x:false}") private boolean invertX;

    private volatile NodeId xNodeId;
    private volatile double lastX = 0.0;
    private ScheduledExecutorService exec;

    public OpcXSourceImp(OpcUaClientService opc) {
        this.opc = opc;
    }

    @Override
    public synchronized void start() {
        if (exec != null && !exec.isShutdown()) return;
        exec = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r, "opc-xsource-sampler");
            t.setDaemon(true);
            return t;
        });
        final int p = Math.max(1, samplePeriodMs);
        exec.scheduleAtFixedRate(this::sampleOnceSafe, 0L, p, TimeUnit.MILLISECONDS);
        log.info("[XSource] started, period={} ms | coordEnabled={} (xScale={}, xOffset={}, invertX={})",
                p, coordEnabled, xScale, xOffset, invertX);
    }

    @Override
    public synchronized void stop() {
        if (exec != null) {
            exec.shutdownNow();
            exec = null;
            log.info("[XSource] stopped");
        }
    }

    @PreDestroy
    public void shutdown() { stop(); }

    @Override
    public double nearestAtHostNs(long hostNs, long tolNs, double fallbackLast) {
        double x = xBuff.nearest(hostNs, tolNs);
        return Double.isNaN(x) ? fallbackLast : x;
    }

    private void sampleOnceSafe() {
        try {
            if (xNodeId == null) xNodeId = opc.validateNodeId(xNodeIdStr);
            Object v = opc.readSingleNode(xNodeId);
            double raw = Double.parseDouble(String.valueOf(v));
            double plc = transformXToPlc(raw);
            long now = System.nanoTime();
            xBuff.add(now, plc);
            lastX = plc;
        } catch (Exception ignore) { /* 降噪：采样失败跳过一拍 */ }
    }

    /** PLC原始X → 扫描截面 X（统一到 PLC 坐标，含安装偏移） */
    private double transformXToPlc(double x) {
        if (!coordEnabled) return x;
        if (invertX) x = -x;
        x = x * xScale;
        x = x + xOffset;   // 安装偏移
        return x;
    }

    /** 提供给策略内部可选获取“上一次有效X” */
    public double lastX() { return lastX; }
}
